package db

import (
	"database/sql"
	"fmt"
	"git.sr.ht/~mil/mobroute/src/logger"
	"git.sr.ht/~mil/mobroute/src/utilcache"
	"git.sr.ht/~mil/mobroute/src/utilfuncs"
	"git.sr.ht/~mil/mobsql"
	"github.com/jmoiron/sqlx"
	sqlite3 "github.com/mattn/go-sqlite3"
	"time"
)

type DBConnection struct {
	DB                              *sqlx.DB
	QueryTime                       *time.Time
	QuerySources                    []int
	QuerySourcesFilterExtractedFrom *mobsql.SourcesetFilter
}

func queryToSlice[T any](c *DBConnection, query string, params []any, intoSlice *[]T) error {
	logger.Infof("Loading %s (%v) to memory", query, params)
	defer func() {
		logger.Infof("Loaded %s (%v) to memory ok (len=%d)", query, params, len(*intoSlice))
	}()

	var item T
	if rows, err := c.DB.Queryx(query, params...); err != nil {
		return err
	} else {
		for rows.Next() {
			if err := rows.StructScan(&item); err != nil {
				return err
			}
			*intoSlice = append(*intoSlice, item)
		}
	}
	return nil
}

func dbPath() string {
	return utilcache.CacheDir("") + "/sqlite.db"
}

func ptr[T any](a T) *T {
	return &a
}

func Initialize(mobsqlFilter *mobsql.SourcesetFilter, fromTime *time.Time, logInfo, logWarn, logDebug bool) (*DBConnection, error) {
	var (
		sqlDriverName = "sqlite3_mobroute_extensions"
		mobsqlConfig  = mobsql.Config{
			SQLiteDBPath: ptr(dbPath()), LogInfo: logInfo, LogWarn: logWarn, LogDebug: logDebug,
		}
		sourceIDs = []int{}
	)

	// Load sources to DB via MobSQL API
	if sources, err := mobsql.Status(*mobsqlFilter, &mobsqlConfig); err != nil {
		return nil, err
	} else if len(sources) < 1 {
		return nil, fmt.Errorf("Mobsql filter %v didn't return atleast 1 sources!", mobsqlFilter)
	} else if _, err := mobsql.Load(*mobsqlFilter, &mobsqlConfig); err != nil {
		return nil, fmt.Errorf("Fail to load from mobsql: %v", err)
	} else if _, err := mobsql.Compute(*mobsqlFilter, &mobsqlConfig); err != nil {
		return nil, fmt.Errorf("Fail to compute from mobsql: %v", err)
	} else {
		for _, s := range sources {
			sourceIDs = append(sourceIDs, s.Mdbid)
		}
	}

	// Open DB and return
	if !utilfuncs.StringInArray(sql.Drivers(), sqlDriverName) {
		sql.Register(sqlDriverName, &sqlite3.SQLiteDriver{
			ConnectHook: func(conn *sqlite3.SQLiteConn) error {
				dbFuncs := map[string]any{
					"ext_timesecformat":   dbfunc_ext_timesecformat,
					"ext_haversinedistkm": dbfunc_ext_haversinedistkm,
				}
				for dbfuncname, cb := range dbFuncs {
					if err := conn.RegisterFunc(dbfuncname, cb, true); err != nil {
						return err
					}
				}
				return nil
			},
		})
	}

	if db, err := sqlx.Open("sqlite3_mobroute_extensions", dbPath()); err != nil {
		return nil, err
	} else {
		return &DBConnection{
			DB:                              db,
			QueryTime:                       fromTime,
			QuerySources:                    sourceIDs,
			QuerySourcesFilterExtractedFrom: mobsqlFilter,
		}, nil
	}
}
